home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 43
/
Aminet 43 (2001)(GTI - Schatztruhe)[!][Jun 2001].iso
/
Aminet
/
comm
/
tcp
/
AmiVNC.lha
/
AmiVNC
/
amivnc.c
< prev
next >
Wrap
C/C++ Source or Header
|
2001-03-02
|
73KB
|
1,667 lines
// AmiVNC - Amiga experimental VNC server - Protocol ORL version 3.3
// Includes ***********************************************
// Needed to compile : SDK Cybergraphx 4.1 & SDK AmiTCP 4.3
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/IntuitionBase.h>
#include <intuition/intuition.h>
#include <graphics/gfxmacros.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxbase.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#include <clib/utility_protos.h>
#include <clib/alib_protos.h>
#include <clib/commodities_protos.h>
#include <clib/icon_protos.h>
#include <dos/dostags.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <lineread.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <filedefs.h>
#include <signal.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <libraries/commodities.h>
#include <clib/socket_protos.h>
#include <clib/netlib_protos.h>
#include <proto/socket.h>
#ifndef PLANAR
#include <cybergraphx/cybergraphics.h>
#include <proto/cybergraphics.h>
#endif
// Specific Includes ***********************************************
#include "rfbproto.h" // Structures & constants for VNC protocol
#include "vncauth.h" // Prototypes for authentication
// Constants ***********************************************
#define FALSE 0 // What's that ?
#define TRUE 1 // And this ?
#define XDC_PORT 5900 // Port # for bind()
#define XDC_TILE 32 // Tile size
#define XDC_C_VBUF (1L << 0) // Bitmask for pBuffer allocation
#define XDC_C_MSOCK (1L << 1) // Bitmask for listen socket open
#define XDC_C_CSOCK (1L << 2) // Bitmask for client socket open
#define XDC_C_MAXDEPTH 4 // Max. raster depth : 4 bytes / pixel
#define XDC_LOGFILE "T:AmiVNC.log" // Log file name
#undef PARANO
// Common messages
char XDC_ID[] = "AmiVNC 0.1.0 Mar 1st 2001 (StormC4/GCC)";
char XDC_SEND[] = "send() error";
char XDC_RECV[] = "recv() error";
// Global variables ***********************************************
LONG iDuplicateSocketKey; // Client socket key for transmitting to child process
ULONG uOpened = NULL; // Opened resources, to be closed on exit
BOOL bDie = FALSE, *pDie = &bDie; // TRUE : processes are to exit (Argh, very trashy)
BOOL bSession = FALSE; // TRUE : clientsession opened
char cPassword[MAXPWLEN + 1], // Password
*pPreStart = NULL, // User command to execute when connection accepted
*pPostStop = NULL; // User command to execute when session closes
LONG iMasterSocket, // Listening socket for incoming connections
iClientSocket; // Client socket accept()ed
UBYTE *pBuffer = NULL, // Reference buffer for screen change detection
*P2CBuffer = NULL; // Temp buffer for ReadPixelArray8() in planar2chunky
char *sPWFile = "S:AmiVNC.pwd"; // Password file name
FILE *fLog; // Logfile
struct sockaddr_in cliAddr; // To find client IP address. Must be addressable for getpeername.
struct BitMap *pBM = NULL; // Temp. BitMap for temp. RastPort for p2c
extern struct IntuitionBase *IntuitionBase;
struct Library *SocketBase;
// Free all resources allocated to a client session ***********************************************
void vCleanSession(char *cMsg, long lCode)
{
// Tell the child process to exit (as we are the main process,
// we can use bDie, which is in our address space)
if (!bDie)
{
bDie = TRUE;
Delay(125);
}
// Close everything opened
if (uOpened & XDC_C_VBUF) { free(pBuffer); pBuffer = NULL; uOpened ^= XDC_C_VBUF; }
if (pBM) { FreeBitMap(pBM); pBM = NULL; }
if (P2CBuffer) { free(P2CBuffer); P2CBuffer = NULL; }
if (uOpened & XDC_C_CSOCK)
{
CloseSocket(iClientSocket);
uOpened ^= XDC_C_CSOCK;
// Execute post-close user command (if any)
if (bSession && pPostStop)
{
char cCommand[128];
sprintf(cCommand, pPostStop, Inet_NtoA(cliAddr.sin_addr.s_addr));
SystemTags(cCommand, SYS_Asynch, TRUE, TAG_DONE);
bSession = FALSE;
}
}
fprintf(fLog, "AmiVNC - %s (%ld)\nAmiVNC session closed\n", cMsg, lCode);
}
// Free all resources allocated to main process and exit ***********************************************
void vCleanExit(char *cMsg, long lCode)
{
// Free tooltype ArgArray initialized by ArgArrayInit()
// ArgArrayDone();
vCleanSession(cMsg, lCode);
if (uOpened & XDC_C_MSOCK) { CloseSocket(iMasterSocket); uOpened ^= XDC_C_MSOCK; }
CloseLibrary(SocketBase);
fprintf(fLog, "AmiVNC halted\n");
if (fLog != stdout) fclose(fLog);
exit(0L);
}
// Free all resources allocated to main process and exit ***********************************************
void vCleanSig(int iCode)
{
vCleanExit("Break CTRL-C", (long) iCode);
}
// Authentication ***********************************************
BOOL bAuthentify(void)
{
char cChallenge[16], cResponse[16];
CARD32 c32Value;
BOOL bAuthenticated = TRUE;
int iCnt, iFile;
// Read encoded password in s:AmiVNC.pwd
if (!(iFile = open(sPWFile, O_RDONLY, 0)))
return(FALSE);
iCnt = read(iFile, cPassword, sizeof(cPassword));
close(iFile);
if (iCnt != sizeof(cPassword))
return(FALSE);
// Decode password
vncDecryptPasswd(cPassword, cPassword);
// Authenticate the connection, if required
if (!strlen(cPassword))
{
// Send no-auth-required message
c32Value = rfbNoAuth;
if (!(-1 == (send(iClientSocket,(char *)&c32Value, sizeof(c32Value), 0))))
return FALSE;
return(TRUE);
}
else
{
// Send auth-required message
c32Value = rfbVncAuth;
if (-1 == (send(iClientSocket,(char *)&c32Value, sizeof(c32Value), 0)))
return FALSE;
// Now create a 16-byte challenge
vncRandomBytes((UBYTE *)cChallenge);
// Send the challenge to the client
if (-1 == (send(iClientSocket, cChallenge, sizeof(cChallenge), 0)))
return FALSE;
// Read the response
if (-1 == (recv(iClientSocket, (UBYTE *) cResponse, sizeof(cResponse), 0)))
return FALSE;
// Encrypt the challenge bytes
vncEncryptBytes((UBYTE *)cChallenge, cPassword);
// Compare them to the response
for (iCnt = 0; iCnt < sizeof(cChallenge); iCnt++)
{
if (cChallenge[iCnt] != cResponse[iCnt])
{
bAuthenticated = FALSE;
break;
}
}
// Did the authentication work?
if (!bAuthenticated)
{
c32Value = rfbVncAuthFailed;
send(iClientSocket, (char *)&c32Value, sizeof(c32Value), 0);
return FALSE;
}
else
{
// Tell the client we're ok
c32Value = rfbVncAuthOK;
if (-1 == (send(iClientSocket, (char *)&c32Value, sizeof(c32Value), 0)))
return FALSE;
return(TRUE);
}
}
}
// Incoming messages management thread ***********************************************
void __saveds vProcessIncomes(void)
{
struct IOStdReq *inputReqBlk;
struct MsgPort *inputPort;
struct IEPointerPixel MPos;
struct InputEvent eEvent, *VNCEvent = &eEvent;
struct Screen *pCurrentScreen;
ULONG iLock;
UWORD uQualifier = 0;
rfbClientToServerMsg sCMsg;
BOOL bLClick = FALSE, bMClick = FALSE, bRClick = FALSE;
struct Library *SocketBase;
LONG iSocketChild;
// We have to reopen bsdsocket.library, because it can not be shared between processes
// OpenLibrary can not fail since it would have halted the father before creating us.
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4L)))
{
fprintf(fLog, "vProcessIncomes : cannot reopen bsdsocket.library\n");
*pDie = TRUE;
goto _Nolib;
}
// Obtain the client socket descriptor from the key saved for us by our father
if (-1 == (iSocketChild = ObtainSocket(iDuplicateSocketKey, AF_INET, SOCK_STREAM, 0)))
{
fprintf(fLog, "vProcessIncomes : ObtainSocket() error %d\n", (int) Errno());
*pDie = TRUE;
goto _Nosock;
}
fprintf(fLog, "vProcessIncomes runs on socket %d\n", (int) iSocketChild);
// Create input.device message structure
inputPort = CreatePort(NULL, NULL);
inputReqBlk = (struct IOStdReq *) CreateExtIO(inputPort, sizeof(struct IOStdReq));
OpenDevice("input.device", NULL, (struct IORequest *) inputReqBlk, NULL);
inputReqBlk -> io_Data = (APTR) VNCEvent;
inputReqBlk -> io_Command = IND_WRITEEVENT;
inputReqBlk -> io_Flags = 0;
inputReqBlk -> io_Length = sizeof(struct InputEvent);
// Process incoming messages until main process tells to die.
// Use pointer to bDie because it is not in our address space...
while (!*pDie) // VERY VERY dirty way to know we have to exit...
{
if (-1 == recv(iSocketChild, (UBYTE *) &sCMsg, 1, 0))
{
fprintf(fLog, "vProcessIncomes : recv() error, errno = %d\n", (int) Errno());
*pDie = TRUE;
continue;
}
// Forbid(); // Leave us handle properly our input event
switch(sCMsg.type)
{
case rfbSetPixelFormat : // 0
if (sz_rfbSetPixelFormatMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbSetPixelFormatMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbSetPixelFormat lanbpp %d depth %d bigE %d tc %d redM %d redS %d GreM %d GreS %d BluM %d BluS %d\n",
sCMsg.spf.format.bitsPerPixel,
sCMsg.spf.format.depth,
sCMsg.spf.format.bigEndian,
sCMsg.spf.format.trueColour,
sCMsg.spf.format.redMax,
sCMsg.spf.format.redShift,
sCMsg.spf.format.greenMax,
sCMsg.spf.format.greenShift,
sCMsg.spf.format.blueMax,
sCMsg.spf.format.blueShift
);
#endif // PARANO
break;
}
case rfbFixColourMapEntries : // 1
if (sz_rfbFixColourMapEntriesMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbFixColourMapEntriesMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbFixColourMapEntries\n");
#endif // PARANO
break;
}
case rfbSetEncodings : // 2
if (sz_rfbSetEncodingsMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbSetEncodingsMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbSetEncodings\n");
#endif // PARANO
break;
}
case rfbFramebufferUpdateRequest : // 3
if (sz_rfbFramebufferUpdateRequestMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbFramebufferUpdateRequestMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbFramebufferUpdateRequest inc %d x %d y %d w %d h %d\n",
sCMsg.fur.incremental,
sCMsg.fur.x,
sCMsg.fur.y,
sCMsg.fur.w,
sCMsg.fur.h
);
#endif // PARANO
break;
}
case rfbKeyEvent : // 4
if (sz_rfbKeyEventMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbKeyEventMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbKeyEvent down %d key %d ('%c')\n",
sCMsg.ke.down,
sCMsg.ke.key,
sCMsg.ke.key
);
#endif // PARANO
// Process special keys
if ((sCMsg.ke.down) && (0xFFE0 == (sCMsg.ke.key & 0xFFE0)))
{
if (sCMsg.ke.key == 0xFFE1) uQualifier |= IEQUALIFIER_LSHIFT; // Left Shift
if (sCMsg.ke.key == 0xFFE2) uQualifier |= IEQUALIFIER_RSHIFT; // Right Shift
if (sCMsg.ke.key == 0xFFE3) uQualifier |= IEQUALIFIER_CONTROL; // Left Control
if (sCMsg.ke.key == 0xFFE4) uQualifier |= IEQUALIFIER_CONTROL; // Right Control
if (sCMsg.ke.key == 0xFFE9) uQualifier |= IEQUALIFIER_LALT; // Left Alt
if (sCMsg.ke.key == 0xFFEA) uQualifier |= IEQUALIFIER_RALT; // Right Alt
break;
}
if ((!sCMsg.ke.down) && (0xFFE0 == (sCMsg.ke.key & 0xFFE0)))
{
if (sCMsg.ke.key == 0xFFE1) uQualifier &= ~IEQUALIFIER_LSHIFT; // Left Shift
if (sCMsg.ke.key == 0xFFE2) uQualifier &= ~IEQUALIFIER_RSHIFT; // Right Shift
if (sCMsg.ke.key == 0xFFE3) uQualifier &= ~IEQUALIFIER_CONTROL; // Left Control
if (sCMsg.ke.key == 0xFFE4) uQualifier &= ~IEQUALIFIER_CONTROL; // Right Control
if (sCMsg.ke.key == 0xFFE9) uQualifier &= ~IEQUALIFIER_LALT; // Left Alt
if (sCMsg.ke.key == 0xFFEA) uQualifier &= ~IEQUALIFIER_RALT; // Right Alt
break;
}
// Do nothing on key release
if (!sCMsg.ke.down) break;
// Process cursor-related keys
if (0xFF50 == (sCMsg.ke.key & 0xFF50))
{
VNCEvent -> ie_Class = IECLASS_RAWKEY;
VNCEvent -> ie_Qualifier = 0;
switch(sCMsg.ke.key & 0x000F)
{
case 0 : // Home
VNCEvent -> ie_Code = 0x3D;
break;
case 1 : // Left
VNCEvent -> ie_Code = 0x4F;
break;
case 2 : // Up
VNCEvent -> ie_Code = 0x4C;
break;
case 3 : // Right
VNCEvent -> ie_Code = 0x4E;
break;
case 4 : // Down
VNCEvent -> ie_Code = 0x4D;
break;
case 5 : // Page up
VNCEvent -> ie_Code = 0x3F;
break;
case 6 : // Page down
VNCEvent -> ie_Code = 0x1F;
break;
case 7 : // End
VNCEvent -> ie_Code = 0x1D;
break;
default :
break;
}
}
else
{
// Map out other 0xFF'ed keys
sCMsg.ke.key &= 0xFF;
// Find rawkey event out of key ; if we can not, reject key
if (!InvertKeyMap((ULONG) sCMsg.ke.key, VNCEvent, NULL))
break;
}
// Apply qualifier if possible
if (!strchr("~`#5(89", (int) sCMsg.ke.key)) switch(uQualifier)
{
case IEQUALIFIER_CONTROL :
case IEQUALIFIER_LSHIFT :
case IEQUALIFIER_RSHIFT :
VNCEvent -> ie_Qualifier = uQualifier;
break;
case IEQUALIFIER_LALT :
VNCEvent -> ie_Qualifier = IEQUALIFIER_LCOMMAND;
break;
case IEQUALIFIER_RALT | IEQUALIFIER_CONTROL :
VNCEvent -> ie_Qualifier = IEQUALIFIER_RCOMMAND;
break;
}
// Send key into input.device
DoIO((struct IORequest *) inputReqBlk);
break;
}
case rfbPointerEvent : // 5
if (sz_rfbPointerEventMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbPointerEventMsg - 1, 0))
{
#ifdef PARANO
fprintf(fLog, "C -> S : rfbPointerEvent button %d x %d y %d\r",
sCMsg.pe.buttonMask,
sCMsg.pe.x,
sCMsg.pe.y
);
#endif // PARANO
/* Set up InputEvent fields */
VNCEvent -> ie_NextEvent = NULL;
#if 0
MPos.iepp_Screen = IntuitionBase -> FirstScreen;
MPos.iepp_Position.X = sCMsg.pe.x;
MPos.iepp_Position.Y = sCMsg.pe.y;
VNCEvent -> ie_EventAddress = (APTR)&MPos;
VNCEvent -> ie_Class = IECLASS_NEWPOINTERPOS;
VNCEvent -> ie_SubClass = IESUBCLASS_PIXEL;
/* absolute positioning */
VNCEvent -> ie_Qualifier = NULL;
VNCEvent -> ie_Qualifier = uQualifier;
#else
VNCEvent -> ie_Class = IECLASS_POINTERPOS;
iLock = LockIBase(0);
pCurrentScreen = IntuitionBase -> FirstScreen;
UnlockIBase(iLock);
VNCEvent -> ie_X = (UWORD) ((float) sCMsg.pe.x * 650. / (float) (
#ifndef PLANAR
GetCyberMapAttr(pCurrentScreen -> RastPort.BitMap, CYBRMATTR_WIDTH)
#else
GetBitMapAttr(pCurrentScreen -> RastPort.BitMap, BMA_WIDTH)
#endif
- 1));
VNCEvent -> ie_Y = (UWORD) ((float) sCMsg.pe.y * 490. / (float) (
#ifndef PLANAR
GetCyberMapAttr(pCurrentScreen -> RastPort.BitMap, CYBRMATTR_HEIGHT)
#else
GetBitMapAttr(pCurrentScreen -> RastPort.BitMap, BMA_HEIGHT)
#endif
- 1));
VNCEvent -> ie_Qualifier = uQualifier;
#endif
// Process mouse buttons...
VNCEvent -> ie_Code = IECODE_NOBUTTON;
// Left button
if ((sCMsg.pe.buttonMask) & 1)
{
if (bLClick)
VNCEvent -> ie_Code = IECODE_NOBUTTON;
else
{
bLClick = TRUE; VNCEvent -> ie_Code = IECODE_LBUTTON; uQualifier |= IEQUALIFIER_LEFTBUTTON;
}
}
else if (bLClick) { bLClick = FALSE; VNCEvent -> ie_Code = IECODE_LBUTTON | IECODE_UP_PREFIX; uQualifier &= ~IEQUALIFIER_LEFTBUTTON; }
// Middle button
if ((sCMsg.pe.buttonMask) & 2)
{
if (bMClick)
VNCEvent -> ie_Code = IECODE_NOBUTTON;
else
{
bMClick = TRUE; VNCEvent -> ie_Code = IECODE_MBUTTON; uQualifier |= IEQUALIFIER_MIDBUTTON;
}
}
else if (bMClick) { bMClick = FALSE; VNCEvent -> ie_Code = IECODE_MBUTTON | IECODE_UP_PREFIX; uQualifier &= ~IEQUALIFIER_MIDBUTTON; }
// Right button
if ((sCMsg.pe.buttonMask) & 4)
{
if (bRClick)
VNCEvent -> ie_Code = IECODE_NOBUTTON;
else
{
bRClick = TRUE; VNCEvent -> ie_Code = IECODE_RBUTTON; uQualifier |= IEQUALIFIER_RBUTTON;
}
}
else if (bRClick) { bRClick = FALSE; VNCEvent -> ie_Code = IECODE_RBUTTON | IECODE_UP_PREFIX; uQualifier &= ~IEQUALIFIER_RBUTTON; }
// Insert event into input.device stream
DoIO((struct IORequest *) inputReqBlk);
break;
}
case rfbClientCutText : // 6
if (sz_rfbClientCutTextMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbClientCutTextMsg - 1, 0))
{
char *cText = (char *) malloc((unsigned) (sCMsg.cct.length + 1));
recv(iSocketChild, (UBYTE *) cText, (int) (sCMsg.cct.length), 0);
cText[sCMsg.cct.length] = 0;
#ifdef PARANO
fprintf(fLog, "C -> S : rfbClientCutText '%s'\n", cText);
#endif // PARANO
break;
}
default :
#ifdef PARANO
fprintf(fLog, "vProcessIncomes : msg type %d unknown\n", sCMsg.type);
#endif // PARANO
break;
}
// Permit();
}
CloseSocket(iSocketChild);
if (inputReqBlk)
{
CloseDevice((struct IORequest *) inputReqBlk);
DeleteExtIO((struct IORequest *) inputReqBlk);
}
if (inputPort) DeletePort(inputPort);
_Nosock:
CloseLibrary(SocketBase);
_Nolib:
fprintf(fLog, "vProcessIncomes halted\n");
}
// Main entry point ***********************************************
int main(int argc, char **argv)
{
register int iCnt, jCnt;
BPTR pFile;
int iMajor, iMinor, iWidth, iHeight, iDepth, iSize, iMode, iPort, iUpdate = 0, iRShift = 8, iGShift = 16, iBShift = 24;
unsigned long uLimit = 0xFFFFFFFF, uSize, uCnt, uTotal;
struct sockaddr_in sAddr; // Local address for bind()
struct Screen *pActiveScreen, *pNewScreen;
rfbProtocolVersionMsg mProtVerMsg;
rfbServerInitMsg mSerInitMsg;
rfbFramebufferUpdateMsg mFBUpdMsg;
rfbFramebufferUpdateRectHeader mFBRMsg;
long iDelay = 50;
CARD8 c8;
BOOL bBig = FALSE, bFastMode, bPlanar, bVVA = FALSE;
register UBYTE *pRaster; // True WB screen raster
static UBYTE uTile[XDC_TILE * XDC_TILE * XDC_C_MAXDEPTH]; // 3 Byte/pixel RGB Raster tile for client screen updates
static UBYTE uCTile[XDC_TILE * XDC_TILE]; // Chunky tile for planar comparisons
char cLog[80], *pC;
ULONG uLock;
UBYTE **ArgArray;
UWORD CLUT[256]; // 16 bit Planar2Chunky color lookup table (pen number -> RGB16PC values)
UBYTE CLUT8[256]; // 8 bit Planar2Chunky color lookup table (pen number -> BGR233 values)
struct RastPort TempRP; // Temp. RastPort for ReadPixelArray8
#ifndef PLANAR
struct Library *CGXlib = OpenLibrary("cgxsystem.library", 40L);
#endif
fLog = stdout;
if (!(SocketBase = OpenLibrary("bsdsocket.library", 4L)))
{
fprintf(fLog, "main : cannot open bsdsocket.library\n");
exit;
}
iPort = XDC_PORT;
strcpy(cLog, XDC_LOGFILE);
signal(SIGINT, vCleanSig);
// Process AmiVNC icon tooltypes
#ifndef __PPC__
ArgArray = ArgArrayInit((long) argc, (UBYTE **) argv);
if (pC = FindToolType(ArgArray, "PORT")) iPort = atoi(pC);
if (pC = FindToolType(ArgArray, "BIGENDIAN")) bBig = (tolower(*pC) == 't') ? TRUE : FALSE;
if ((pC = FindToolType(ArgArray, "LIMIT")) && (atoi(pC))) uLimit = atoi(pC);
if (pC = FindToolType(ArgArray, "RSHIFT")) iRShift = atoi(pC);
if (pC = FindToolType(ArgArray, "GSHIFT")) iGShift = atoi(pC);
if (pC = FindToolType(ArgArray, "BSHIFT")) iBShift = atoi(pC);
if (pC = FindToolType(ArgArray, "LOGFILE")) strcpy(cLog, pC);
if (pC = FindToolType(ArgArray, "PRESTART")) pPreStart = pC;
if (pC = FindToolType(ArgArray, "POSTSTOP")) pPostStop = pC;
if (pC = FindToolType(ArgArray, "VERBOSE")) cLog[0] = 0;
if (pC = FindToolType(ArgArray, "VVA")) bVVA = TRUE;
if (pC = FindToolType(ArgArray, "DELAY")) iDelay = atoi(pC);
#endif
// Process command line arguments (if any)
if (argc > 1) while (*++argv)
{
switch(tolower((*argv)[1]))
{
// *** Force password change and exit
case 'p' :
strncpy(cPassword, *argv + 2, MAXPWLEN);
cPassword[MAXPWLEN] = '\0';
pC = strchr(cPassword, ' ');
if (pC) *pC = '\0';
for (iCnt = strlen(cPassword) ; iCnt < MAXPWLEN ; iCnt++)
cPassword[iCnt] = '\0';
vncEncryptPasswd(cPassword, cPassword);
if (!(pFile = Open(sPWFile, MODE_NEWFILE)))
vCleanExit("main : creat('s:AmiVNC.pwd') error", 0);
Write(pFile, cPassword, sizeof(cPassword));
Close(pFile);
vCleanExit("main : Passwd stored", 0);
break;
// *** Force another port than the default 5900
case 's' :
iPort = atoi(*argv + 2);
break;
// *** Force BigEndian flag in mSerInitMsg
case 'e' :
bBig = TRUE;
break;
// *** Limit netblocksize for initial screen update
case 'l' :
uLimit = atoi(*argv + 2);
break;
// *** Force Red / Green / Blue bitshifts in mSerInitMsg
// (play with this if you have color problems, legal values
// for shifts are 0, 8, 16 and 24)
case 'r' :
iRShift = atoi(*argv + 2);
break;
case 'g' :
iGShift = atoi(*argv + 2);
break;
case 'b' :
iBShift = atoi(*argv + 2);
break;
// Force verbose mode to sdtout
case 'v' :
cLog[0] = 0;
break;
// Force VVA compatible (BGR233) pixel encoding
case 'a' :
bVVA = TRUE;
break;
// *** Force delay between screen scans
case 'd' :
iDelay = atoi(*argv + 2);
break;
default :
printf("main : Arg [%s] ignored\n", *argv);
break;
}
}
// If log not forced to stdout, open log file
if (cLog[0])
{
fLog = fopen(cLog, "w");
if (!fLog)
{
fLog = stdout;
vCleanExit("main : Log file open error", 0);
}
}
// Welcome...
fprintf(fLog, "%s\n(c) 1999-2001 stephane.guillard@steria.fr\n", XDC_ID);
#ifdef PLANAR
fprintf(fLog, "Planar only version\n");
#endif
// Print configurable parameter values
fprintf(fLog, "main : parameter values :\n");
fprintf(fLog, " - listen port set to %d\n", iPort);
fprintf(fLog, " - endian set to %s\n", bBig ? "big" : "little");
fprintf(fLog, " - initial xfer limit set to %08lX\n", uLimit);
fprintf(fLog, " - color bitshifts : red %d, green %d, blue %d\n", iRShift, iGShift, iBShift);
fprintf(fLog, " - log file set to %s\n", cLog[0] ? cLog : "stdout");
fprintf(fLog, " - VVA BGR233 encoding : %s\n", bVVA ? "yes" : "no");
fprintf(fLog, " - user command on client login : %s\n", pPreStart ? pPreStart : "none");
fprintf(fLog, " - user command on client logout : %s\n", pPostStop ? pPostStop : "none");
// Prepare listener socket
fprintf(fLog, "main : Creating listener socket\n");
if (-1 == (iMasterSocket = socket(AF_INET, SOCK_STREAM, 0)))
vCleanExit("main : socket() error", Errno());
uOpened |= XDC_C_MSOCK;
sAddr.sin_family=AF_INET;
sAddr.sin_addr.s_addr = 0;
sAddr.sin_port = htons(iPort);
fprintf(fLog, "main : Binding socket on port %d\n", sAddr.sin_port);
if (-1 == (bind(iMasterSocket, (struct sockaddr *) &sAddr, sizeof(sAddr))))
vCleanExit("main : bind() error", Errno());
fprintf(fLog, "main : Listening set on port %d\n", sAddr.sin_port);
if (-1 == (listen(iMasterSocket, 4)))
vCleanExit("main : listen() error", Errno());
_NewSession:
bDie = FALSE;
// Wait for incoming connection and find client IP address (use iDuplicateSocketKey
// as temp. var because at this point it is useless)
fprintf(fLog, "main : Waiting for client connection\n");
iDuplicateSocketKey = sizeof(cliAddr);
if (-1 == (iClientSocket = accept(iMasterSocket, (struct sockaddr *) &cliAddr, &iDuplicateSocketKey)))
vCleanExit("main : accept() error", Errno());
uOpened |= XDC_C_CSOCK;
// Now useless : getpeername(iClientSocket, (struct sockaddr *) &cliAddr, &iDuplicateSocketKey);
fprintf(fLog, "main : accept()ed connection from %s on socket %d\n", Inet_NtoA(cliAddr.sin_addr.s_addr), (int) iClientSocket);
// NegociateProtocolVersion
fprintf(fLog, "main : Negociating protocol version\n");
sprintf((char *) mProtVerMsg, rfbProtocolVersionFormat,
rfbProtocolMajorVersion,
rfbProtocolMinorVersion);
if (-1 == (send(iClientSocket, (UBYTE *) mProtVerMsg, sz_rfbProtocolVersionMsg, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
mProtVerMsg[12] = 0;
if (-1 == (recv(iClientSocket, mProtVerMsg, sz_rfbProtocolVersionMsg, 0)))
{ vCleanSession(XDC_RECV, Errno()); goto _NewSession; }
sscanf((char *) mProtVerMsg, rfbProtocolVersionFormat, &iMajor, &iMinor);
fprintf(fLog, "main : Protocol supported by server : %d.%d, by client : %d.%d\n", rfbProtocolMajorVersion, rfbProtocolMinorVersion, iMajor, iMinor);
// Check protocol version
if (iMajor > rfbProtocolMajorVersion)
{ vCleanSession("main : unknown protocol", 0); goto _NewSession; }
// Authenticate DES
fprintf(fLog, "main : Authentication\n");
if (FALSE == bAuthentify())
{ vCleanSession("main : Authenticate error", 0); goto _NewSession; }
// ClientInit
fprintf(fLog, "main : ClientInitialisation\n");
if (-1 == (recv(iClientSocket, &c8, sizeof(c8), 0)))
{ vCleanSession(XDC_RECV, Errno()); goto _NewSession; }
fprintf(fLog, "main : SharedFlag = %d (multiple clients : %s)\n", c8, c8 ? "Yes" : "No");
// Grab active screen
pActiveScreen = IntuitionBase -> FirstScreen;
// Check if planar Amiga screen
bPlanar = GetBitMapAttr(pActiveScreen -> RastPort.BitMap, BMA_FLAGS) & BMF_STANDARD ? TRUE : FALSE;
fprintf(fLog, "main : Screen set to [%s] (%s)\n", pActiveScreen -> Title, bPlanar ? "planar" : "RTG");
// Get screen info (mode, width, height, depth, linear address)
if (!bPlanar) // RTG chunky linear modes
{
#ifndef PLANAR
if (GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_ISCYBERGFX)
&& GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_ISLINEARMEM))
{
// Get screenmode, width, height, depth
iMode = GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_PIXFMT);
iWidth = GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_WIDTH); // X size
iHeight = GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_HEIGHT); // Y size
iDepth = GetCyberMapAttr(pActiveScreen -> RastPort.BitMap, CYBRMATTR_BPPIX); // Depth is in Bytes / pixel
// Grab chunky raster pointer (dirty but EFFICIENT !)
UnLockBitMap(LockBitMapTags(pActiveScreen -> RastPort.BitMap,
(ULONG) LBMI_BASEADDRESS, (ULONG) &uCnt,
TAG_DONE));
pRaster = (UBYTE *) uCnt;
// Calculate total byte size of screen raster (adjust to 2 byte / pixel if CLUT, will realloc later)
iSize = iWidth * iHeight * (iDepth > 1 ? iDepth : 2);
fprintf(fLog, "main : RTG mode %s/%ld ", CGXlib ? "CGFx" : "Pic96", (LONG) iMode);
}
else // mode unsupported (neither planar neither RTG chunky linear)
#endif
{ vCleanSession("main : screenmode not supported", 0); goto _NewSession; }
}
else // planar (non-RTG Amiga Classic chipset planar modes)
{
// Get width, height, depth
#ifndef PLANAR
iMode = 0xFF; // To remember this is a non-RTG screen to set up frame buffer format for ServerInit
#endif
iWidth = GetBitMapAttr(pActiveScreen -> RastPort.BitMap, BMA_WIDTH); // X size
iHeight = GetBitMapAttr(pActiveScreen -> RastPort.BitMap, BMA_HEIGHT); // Y size
iDepth = GetBitMapAttr(pActiveScreen -> RastPort.BitMap, BMA_DEPTH); // Depth is in Bits / pixel
// Force size to 2 X Y (each planar pixel will be sent as a 2 byte RGB4 chunky pixel)
iSize = iWidth * iHeight * (bVVA ? 1 : 2);
// Screen memory is not linear addressable (but we rather have a raster per bitplane)
pRaster = NULL;
fprintf(fLog, "main : planar mode ");
}
fprintf(fLog, "@ 0x%08lX, %dx%d, %d %cpp, size %d\n", (ULONG) pRaster, iWidth, iHeight, iDepth, bPlanar ? 'b' : 'B', bPlanar ? iWidth * iHeight * iDepth / 8 : iSize);
// Fast mode : 2 byte pixel buffer :
// - planar Amiga screens
// OR
// - Picasso96 AND 2 byte pixels
// OR
// - 1 byte RTG CLUT pixels
#ifndef PLANAR
bFastMode = ((bPlanar) || ((!CGXlib) && (iDepth == 2)) || ((!bPlanar) && (iDepth == 1)));
#else
bFastMode = TRUE;
#endif
// Allocate pBuffer (reference buffer for screen compare)
// - with the screen size if no VVA, and ((under P96 and Depth < 3) OR if planar)
// - with 4 byte / pixel size if under CGFx or Depth >= 3 or VVA
if (bVVA)
{
if (!(pBuffer = malloc((unsigned) ((bPlanar || (iDepth == 1)) ? iWidth * iHeight : iWidth * iHeight * XDC_C_MAXDEPTH))))
{ vCleanSession("main : malloc() error", 0); goto _NewSession; }
}
else
if (!(pBuffer = malloc((unsigned) (bFastMode ? iSize : iWidth * iHeight * XDC_C_MAXDEPTH))))
{ vCleanSession("main : malloc() error", 0); goto _NewSession; }
uOpened |= XDC_C_VBUF;
// ServerInit : prepare RGB encodings
// if client is VVA, force encoding as BGR233 in all cases, as VVA only knows this encoding
if (bVVA)
{
mSerInitMsg.format.bitsPerPixel = 8;
mSerInitMsg.format.depth = 8;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 7;
mSerInitMsg.format.greenMax = 7;
mSerInitMsg.format.blueMax = 3;
mSerInitMsg.format.redShift = 0;
mSerInitMsg.format.greenShift = 3;
mSerInitMsg.format.blueShift = 6;
}
else
#ifndef PLANAR
if (bFastMode)
{
switch(iMode)
{
case PIXFMT_RGB16PC : // TESTED OK /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: gggbbbbbrrrrrggg */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 16;
mSerInitMsg.format.bigEndian = TRUE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 63;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 11;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 0;
break;
case PIXFMT_RGB15PC : /* HiColor15 (5 bit each), format: gggbbbbb0rrrrrgg */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 15;
mSerInitMsg.format.bigEndian = TRUE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 31;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 10;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 0;
break;
case PIXFMT_RGB16 : /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: rrrrrggggggbbbbb */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 16;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 63;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 11;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 0;
break;
case PIXFMT_RGB15 : /* HiColor15 (5 bit each), format: 0rrrrrgggggbbbbb */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 15;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 31;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 10;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 0;
break;
case PIXFMT_BGR16PC : /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: gggrrrrrbbbbbggg */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 16;
mSerInitMsg.format.bigEndian = TRUE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 63;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 0;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 11;
break;
case PIXFMT_BGR15PC : /* HiColor15 (5 bit each), format: gggrrrrr0bbbbbbgg */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 15;
mSerInitMsg.format.bigEndian = TRUE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 31;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 0;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 10;
break;
case PIXFMT_BGR16 : /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: bbbbbggggggrrrrr */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 16;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 63;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 0;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 11;
break;
case PIXFMT_BGR15 : /* HiColor15 (5 bit each), format: 0bbbbbbgggggrrrrr */
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 15;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 31;
mSerInitMsg.format.greenMax = 31;
mSerInitMsg.format.blueMax = 31;
mSerInitMsg.format.redShift = 0;
mSerInitMsg.format.greenShift = 5;
mSerInitMsg.format.blueShift = 10;
break;
case PIXFMT_LUT8 : /* Chunky CLUT screens, or ... */
case 0xFF : /* planar screens : send 2 byte pixels,aligned as per GetRGB4 : 0000RRRR GGGGBBBB*/
#endif
{
mSerInitMsg.format.bitsPerPixel = 16;
mSerInitMsg.format.depth = 12;
mSerInitMsg.format.bigEndian = FALSE;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax = 15;
mSerInitMsg.format.greenMax = 15;
mSerInitMsg.format.blueMax = 15;
mSerInitMsg.format.redShift = 8;
mSerInitMsg.format.greenShift = 4;
mSerInitMsg.format.blueShift = 0;
}
#ifndef PLANAR
break;
default :
{ vCleanSession("main : RGB mode not supported", 0); goto _NewSession; }
break;
}
}
else // CGFx or Depth > 2 or screen not planar
// then use dumb 24 bpp mode, sent as 32 bpp because VNC protocol forbids 3 byte pixels
{
mSerInitMsg.format.bitsPerPixel = 32;
mSerInitMsg.format.depth = 24;
mSerInitMsg.format.bigEndian = bBig;
mSerInitMsg.format.trueColour = TRUE;
mSerInitMsg.format.redMax =
mSerInitMsg.format.greenMax =
mSerInitMsg.format.blueMax = 255;
mSerInitMsg.format.redShift = iRShift;
mSerInitMsg.format.greenShift = iGShift;
mSerInitMsg.format.blueShift = iBShift;
}
#endif
// Finish serverinit and send it
mSerInitMsg.framebufferWidth = iWidth;
mSerInitMsg.framebufferHeight = iHeight;
mSerInitMsg.nameLength = sizeof(XDC_ID);
if (-1 == (send(iClientSocket, (UBYTE *) &mSerInitMsg, sizeof(mSerInitMsg), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
if (-1 == (send(iClientSocket, (UBYTE *) XDC_ID, sizeof(XDC_ID), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
// Initial Screen update : header...
mFBUpdMsg.type = rfbFramebufferUpdate;
mFBUpdMsg.nRects = 1;
if (-1 == (send(iClientSocket, (UBYTE *) &mFBUpdMsg, sizeof(mFBUpdMsg), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
// Initial Screen update : 1 rectangle for whole screen...
mFBRMsg.r.x = 0;
mFBRMsg.r.y = 0;
mFBRMsg.r.w = iWidth;
mFBRMsg.r.h = iHeight;
mFBRMsg.encoding = rfbEncodingRaw;
if (-1 == (send(iClientSocket, (UBYTE *) &mFBRMsg, sizeof(mFBRMsg), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
// Copy raster into buffer for send() and for ulterior compares
if ((bFastMode) && (!bVVA)) // Planar or Pic96/2 byte pixels, no VVA
{
#ifndef PLANAR
if (bPlanar) // planar screen
{
#endif
// We have to :
// - ReadPixelArray8 the whole screen,
// - make a (256 entries max.) color lookup table (CLUT) out of screen colormap
// - Then encode each pixel with GetRGB4 and the CLUT, as a 16 bit pixel array to send to the client
P2CBuffer = malloc((unsigned) (iWidth * iHeight)); // 1 byte per pixel : pen number
// Allocate bitmap for temp. rastport for ReadPixelArray8()
pBM = AllocBitMap((unsigned long) iWidth, (unsigned long) 1, (unsigned long) iDepth,
BMF_CLEAR | BMF_DISPLAYABLE, pActiveScreen -> RastPort.BitMap); // Temp. BitMap for temp. RastPort
// Initialize temporary rastport necessary for ReadPixelArray8()
InitRastPort(&TempRP);
TempRP.Layer = NULL;
TempRP.BitMap = pBM;
// Fill color lookup table with ready-to-send 16 bit RGB values
jCnt = 1 << iDepth;
for (iCnt = 0 ; iCnt < jCnt ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iCnt] = (wG << 4 | wB) << 8 | wR;
}
// Read our chunky clut-index array from planar raster
ReadPixelArray8(&(pActiveScreen -> RastPort),
(unsigned long) 0, (unsigned long) 0, (unsigned long) iWidth - 1, (unsigned long) iHeight - 1,
P2CBuffer,
&TempRP
);
// Make up the RBG chunky buffer
for (jCnt = 0 ; jCnt < iHeight ; jCnt++)
for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
*((UWORD *) (pBuffer + (iCnt + jCnt * iWidth) * 2)) = CLUT[P2CBuffer[iCnt + jCnt * iWidth]];
#ifndef PLANAR
}
else
if (iDepth == 1) // 8 bit pixels, CLUT, RTG chunky
{
// Fill color lookup table with 256 ready-to-send 16 bit RGB values
for (iCnt = 0 ; iCnt < 256 ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iCnt] = (wG << 4 | wB) << 8 | wR;
}
// Make up the RBG chunky buffer
for (jCnt = 0 ; jCnt < iHeight ; jCnt++)
for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
*((UWORD *) (pBuffer + (iCnt + jCnt * iWidth) * 2)) = CLUT[pRaster[iCnt + jCnt * iWidth]];
}
else // Picasso 96 2 byte pixel screen
{
// simply send raster memory (as 16 bit pixels)
memcpy(pBuffer, pRaster, (unsigned) iSize);
}
#endif
}
else // neither planar nor CLUT nor Pic96/2byte pixels (thus 3 or more byte pixels or CGFx > 1 Bpp),
// or VVA in all cases
{
#ifndef PLANAR
if (bPlanar) // planar screen
{
#endif
// We have to :
// - ReadPixelArray8 the whole screen,
// - make a (256 entries max.) color lookup table (CLUT) out of screen colormap
// - Then encode each pixel with GetRGB4 and the CLUT, as a 8 bit BGR233 pixel array to send to the client
P2CBuffer = malloc((unsigned) (iWidth * iHeight)); // 1 byte per pixel : pen number
// Allocate bitmap for temp. rastport for ReadPixelArray8()
pBM = AllocBitMap((unsigned long) iWidth, (unsigned long) 1, (unsigned long) iDepth,
BMF_CLEAR | BMF_DISPLAYABLE, pActiveScreen -> RastPort.BitMap); // Temp. BitMap for temp. RastPort
// Initialize temporary rastport necessary for ReadPixelArray8()
InitRastPort(&TempRP);
TempRP.Layer = NULL;
TempRP.BitMap = pBM;
// Fill color lookup table with ready-to-send BGR233 values
jCnt = 1 << iDepth;
for (iCnt = 0 ; iCnt < jCnt ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iCnt] = (wB << 3 | wG) << 3 | wR;
}
// Read our chunky clut-index array from planar raster
ReadPixelArray8(&(pActiveScreen -> RastPort),
(unsigned long) 0, (unsigned long) 0, (unsigned long) iWidth - 1, (unsigned long) iHeight - 1,
P2CBuffer,
&TempRP
);
// Make up the BGR233 chunky buffer
for (jCnt = 0 ; jCnt < iHeight ; jCnt++)
for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
pBuffer[iCnt + jCnt * iWidth] = CLUT8[P2CBuffer[iCnt + jCnt * iWidth]];
#ifndef PLANAR
}
else
if (iDepth == 1) // 8 bit pixels, CLUT, RTG chunky
{
// Fill color lookup table with 256 ready-to-send BGR233 values
for (iCnt = 0 ; iCnt < 256 ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iCnt] = (wB << 3 | wG) << 3 | wR;
}
// Make up the RBG chunky buffer
for (jCnt = 0 ; jCnt < iHeight ; jCnt++)
for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
pBuffer[iCnt + jCnt * iWidth] = CLUT8[pRaster[iCnt + jCnt * iWidth]];
}
else // All RTG modes with depth > 1 byte
{
// We have to make 4 byte pixels out of the raster
ReadPixelArray(pBuffer, // Buffer
(UWORD) 0, (UWORD) 0, // Dest X/Y in buffer
(UWORD) (iWidth * XDC_C_MAXDEPTH), // Byte width of buffer
& (pActiveScreen -> RastPort),
(UWORD) 0, (UWORD) 0, // Source X/Y
(UWORD) iWidth, (UWORD) iHeight, // Source w/h
(UBYTE) RECTFMT_ARGB
);
if (bVVA) // Make up the BGR233 buffer
for (jCnt = 0 ; jCnt < iHeight ; jCnt++)
for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
pBuffer[iCnt + jCnt * iWidth] =
(pBuffer[(iCnt + jCnt * iWidth) * 4 + 1] & 0xE0) >> 5 |
(pBuffer[(iCnt + jCnt * iWidth) * 4 + 2] & 0xE0) >> 2 |
(pBuffer[(iCnt + jCnt * iWidth) * 4 + 3] & 0xC0) >> 0;
}
#endif
}
// Send buffer (by uLimit packet size if set).
uTotal = (bVVA ? iWidth * iHeight : (bFastMode ? iSize : iWidth * iHeight * XDC_C_MAXDEPTH));
uSize = (uTotal > uLimit ? uLimit : uTotal);
for (uCnt = 0 ; uCnt < uTotal ; uCnt += uSize)
if (-1 == (send(iClientSocket, (UBYTE *) pBuffer + uCnt, (int) uSize, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
#ifndef PLANAR
// if CGFx or depth > 2, reallocate the buffer to actual raster size (eg .3 byte / pixel instead of 4)
if ((!bFastMode) && (iDepth != 4) && ((iDepth == 3) || (CGXlib)))
{
// Reallocate the buffer, resizing it to what's really necessary
if (!(pBuffer = realloc(pBuffer, (unsigned) iSize)))
{ vCleanSession("main : realloc() error", 0); goto _NewSession; }
// Byte copy the screen into the buffer for fast compare
memcpy(pBuffer, pRaster, (unsigned) iSize);
}
if ((bFastMode) && (!bPlanar) && (bVVA) && (iDepth == 2))
{
// Reallocate the buffer, resizing it to what's really necessary
if (!(pBuffer = realloc(pBuffer, (unsigned) iSize)))
{ vCleanSession("main : realloc() error", 0); goto _NewSession; }
// Byte copy the screen into the buffer for fast compare
memcpy(pBuffer, pRaster, (unsigned) iSize);
}
// if planar, reallocate the buffer to iWidth x iDepth for ReadPixelArray8() and store chunky raster
if (bPlanar)
{
#endif
// Reallocate the buffer, resizing it to what's really necessary
if ((!bVVA) && (!(pBuffer = realloc(pBuffer, (unsigned) (iWidth * iHeight)))))
{ vCleanSession("main : realloc() error", 0); goto _NewSession; }
// Byte copy the screen into the buffer for fast compare
memcpy(pBuffer, P2CBuffer, (unsigned) (iWidth * iHeight));
// Free P2CBuffer, which from now on is useless
free(P2CBuffer);
P2CBuffer = NULL;
#ifndef PLANAR
}
// if RTG chunky CLUT, reallocate the buffer to iWidth x iDepth and copy screen raster into it
if ((!bPlanar) && (iDepth == 1))
{
// Reallocate the buffer, resizing it to what's really necessary
if ((!bVVA) && (!(pBuffer = realloc(pBuffer, (unsigned) (iWidth * iHeight)))))
{ vCleanSession("main : realloc() error", 0); goto _NewSession; }
// Byte copy the screen into the buffer for fast compare
memcpy(pBuffer, pRaster, (unsigned) (iWidth * iHeight));
}
#endif
// Preset framebuffer update message X and Y size to XDC_TILE
mFBRMsg.r.w =
mFBRMsg.r.h = XDC_TILE;
// Release a key to the client socket so that child process can grab it
if (-1 == (iDuplicateSocketKey = ReleaseCopyOfSocket(iClientSocket, UNIQUE_ID)))
{ vCleanSession("main : ReleaseCopyOfSocket() error", Errno()); goto _NewSession; }
// Create incoming messages handling child process
if (!CreateNewProcTags(
NP_Entry, vProcessIncomes,
NP_StackSize, 32000,
NP_Name, "AmiVNC handler",
TAG_DONE
))
{ vCleanSession("main : CreateNewProcTags() error", 0); goto _NewSession; }
// Execute user command on accepted connection (if any)
if (pPreStart)
{
char cCommand[128];
sprintf(cCommand, pPreStart, Inet_NtoA(cliAddr.sin_addr.s_addr));
SystemTags(cCommand, SYS_Asynch, TRUE, TAG_DONE);
}
// Session is truly opened now
bSession = TRUE;
// While not end of session (by child or by ourself)
while (!bDie)
{
// If a delay is required, then wait
if (iDelay) Delay(iDelay);
// Search for active screen change
uLock = LockIBase(0);
pNewScreen = IntuitionBase -> FirstScreen;
UnlockIBase(uLock);
if (pNewScreen != pActiveScreen)
{
#ifndef PLANAR
if (bPlanar) // Original Amiga screens
{
#endif
// Check if new screen is also planar and has same dimensions
if ((GetBitMapAttr(pNewScreen -> RastPort.BitMap, BMA_FLAGS) & BMF_STANDARD)
&& (iWidth == GetBitMapAttr(pNewScreen -> RastPort.BitMap, BMA_WIDTH)) // X size
&& (iHeight == GetBitMapAttr(pNewScreen -> RastPort.BitMap, BMA_HEIGHT))) // Y size
{
// Grab new screen
pActiveScreen = pNewScreen;
fprintf(fLog, "main : Screen set to [%s]\n", pActiveScreen -> Title);
// Update depth
iDepth = GetBitMapAttr(pActiveScreen -> RastPort.BitMap, BMA_DEPTH); // Depth is in Bits / pixel
// Update CLUT (or CLUT8 if bVVA)
jCnt = 1 << iDepth;
if (bVVA) // BGR233
for (iCnt = 0 ; iCnt < jCnt ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iCnt] = (wB << 3 | wG) << 3 | wR;
}
else // RGB16PC
for (iCnt = 0 ; iCnt < jCnt ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iCnt] = (wG << 4 | wB) << 8 | wR;
}
// Force client screen update (almost :-)
memset(pBuffer, iWidth * iHeight, 0xFF);
}
else
{ vCleanSession("main : planar screenmode switch not allowed", 0); goto _NewSession; }
#ifndef PLANAR
} // bPlanar
else // Chunky (RTG)
{
if (GetCyberMapAttr(pNewScreen -> RastPort.BitMap, CYBRMATTR_ISCYBERGFX)
&& GetCyberMapAttr(pNewScreen -> RastPort.BitMap, CYBRMATTR_ISLINEARMEM)
&& iWidth == GetCyberMapAttr(pNewScreen -> RastPort.BitMap, CYBRMATTR_WIDTH)
&& iHeight == GetCyberMapAttr(pNewScreen -> RastPort.BitMap, CYBRMATTR_HEIGHT)
&& iDepth == GetCyberMapAttr(pNewScreen -> RastPort.BitMap, CYBRMATTR_BPPIX))
{
// Grab new screen
pActiveScreen = pNewScreen;
fprintf(fLog, "main : Screen set to [%s]\n", pActiveScreen -> Title);
UnLockBitMap(LockBitMapTags(pActiveScreen -> RastPort.BitMap,
LBMI_BASEADDRESS, (ULONG) &uCnt,
TAG_DONE));
pRaster = (UBYTE *) uCnt;
// If depth == 1, update CLUT
if (iDepth == 1)
{
if (bVVA) // BGR233
for (iCnt = 0 ; iCnt < 256 ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iCnt] = (wB << 3 | wG) << 3 | wR;
}
else // RGB16PC
for (iCnt = 0 ; iCnt < 256 ; iCnt++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iCnt);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iCnt] = (wG << 4 | wB) << 8 | wR;
}
// Force client screen update (almost :-)
memset(pBuffer, iWidth * iHeight, 0xFF);
}
}
else
{ vCleanSession("main : chunky screenmode switch not allowed", 0); goto _NewSession; }
} // Chunky
#endif
} // pNewScreen != pActiveScreen
// Search for changes in screen...
for (jCnt = 0 ; jCnt < iHeight; jCnt += XDC_TILE)
{
for (iCnt = 0 ; iCnt < iWidth ; iCnt += XDC_TILE)
{
register int iYtile, iXtile;
BOOL bChange = FALSE;
#ifndef PLANAR
if (!bPlanar) // chunky linear-addressed rasters
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
{
if (memcmp((UBYTE *) (pBuffer) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
(UBYTE *) (pRaster) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
(unsigned) (XDC_TILE * iDepth)))
{
memcpy((UBYTE *) (pBuffer) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
(UBYTE *) (pRaster) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
(unsigned) (XDC_TILE * iDepth));
bChange = TRUE;
}
}
else // planar rasters
{
#endif
// 0 - Once every 10 scans, refresh palette (we have no event here to say palette has changed)
if (!(iUpdate++ % 10))
{
int nColor = 1 << iDepth, iColor;
if (bVVA)
for (iColor = 0 ; iColor < 256 ; iColor++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iColor);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iColor] = (wB << 3 | wG) << 3 | wR;
}
else
for (iColor = 0 ; iColor < nColor ; iColor++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iColor);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iColor] = (wG << 4 | wB) << 8 | wR;
}
}
// 1 - get a chunky 32x32 1 byte clut entry buffer
ReadPixelArray8(&(pActiveScreen -> RastPort),
(unsigned long) iCnt, (unsigned long) jCnt, (unsigned long) iCnt + XDC_TILE - 1, (unsigned long) jCnt + XDC_TILE - 1,
uCTile,
&TempRP
);
// 2 - then compare it to reference buffer. if !=, remember to update client and store new.
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
{
if (memcmp((UBYTE *) (pBuffer) + (iCnt + (jCnt + iYtile) * iWidth),
(UBYTE *) (uCTile) + (iYtile * XDC_TILE),
XDC_TILE))
{
memcpy((UBYTE *) (pBuffer) + (iCnt + (jCnt + iYtile) * iWidth),
(UBYTE *) (uCTile) + (iYtile * XDC_TILE),
XDC_TILE);
bChange = TRUE;
}
}
#ifndef PLANAR
}
#endif
// If change found, send the tile
if (bChange)
{
if (-1 == (send(iClientSocket, (UBYTE *) &mFBUpdMsg, sizeof(mFBUpdMsg), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
mFBRMsg.r.x = iCnt;
mFBRMsg.r.y = jCnt;
// Send framebuffer update header
if (-1 == (send(iClientSocket, (UBYTE *) &mFBRMsg, sizeof(mFBRMsg), 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
// Send update data
#ifndef PLANAR
if (bPlanar)
{
#endif
if (bVVA)
{
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile++)
uTile[iXtile + iYtile * XDC_TILE] = CLUT8[uCTile[iXtile + iYtile * XDC_TILE]];
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
else
{
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile++)
*((UWORD *) (uTile + (iXtile + iYtile * XDC_TILE) * 2)) = CLUT[uCTile[iXtile + iYtile * XDC_TILE]];
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE * 2, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
#ifndef PLANAR
}
else
if ((!bVVA) && (bFastMode) && (iDepth == 2)) // 2 byte pixels under Pic96 : send directly from buffer (2 is idepth)
{
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
memcpy(uTile + XDC_TILE * iYtile * iDepth,
pRaster + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
(unsigned) (XDC_TILE * iDepth));
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE * 2, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
else
if ((!bVVA) && (iDepth == 1)) // RTG chunky 1 byte pixels
{
// Every 10 update, refresh color lookup table with 256 ready-to-send 16 bit RGB values
if (!(iUpdate++ % 10))
{
int iColor;
for (iColor = 0 ; iColor < 256 ; iColor++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iColor);
ULONG wR = (lColor >> 8) & 0x0f;
ULONG wG = (lColor >> 4) & 0x0f;
ULONG wB = (lColor >> 0) & 0x0f;
CLUT[iColor] = (wG << 4 | wB) << 8 | wR;
}
}
// Make up the RBG chunky buffer
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile ++)
*((UWORD *) (uTile + (iXtile + iYtile * XDC_TILE) * 2)) = CLUT[pBuffer[iCnt + iXtile + (jCnt + iYtile) * iWidth]];
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE * 2, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
else // Dumb screenmode or VVA
{
if (iDepth > 1)
{
ReadPixelArray(uTile, // Buffer
0, 0, // Dest X/Y in buffer
XDC_TILE * XDC_C_MAXDEPTH,
&(pActiveScreen -> RastPort),
iCnt, jCnt, // Source X/Y
XDC_TILE, XDC_TILE, // Source w/h
RECTFMT_ARGB
);
if (bVVA)
{
// Make the BGR233 buffer into uTile
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile ++)
{
uTile[iXtile + iYtile * XDC_TILE] =
(uTile[(iXtile + iYtile * XDC_TILE) * 4 + 1] & 0xE0) >> 5 |
(uTile[(iXtile + iYtile * XDC_TILE) * 4 + 2] & 0xE0) >> 2 |
(uTile[(iXtile + iYtile * XDC_TILE) * 4 + 3] & 0xC0) >> 0;
}
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
else
{
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE * XDC_C_MAXDEPTH, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
}
else // iDepth == 1 and VVA (Depth == 1 and not VVA is trated above)
{
// Once every 10 scans, refresh palette (we have no event here to say palette has changed)
if (!(iUpdate++ % 10))
{
int iColor;
for (iColor = 0 ; iColor < 256 ; iColor++)
{
ULONG lColor = GetRGB4(pActiveScreen -> ViewPort.ColorMap, (long) iColor);
UBYTE wR = (lColor >> 9) & 0x07;
UBYTE wG = (lColor >> 5) & 0x07;
UBYTE wB = (lColor >> 2) & 0x03;
CLUT8[iColor] = (wB << 3 | wG) << 3 | wR;
}
}
// Make up the RBG chunky buffer
for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile ++)
uTile[iXtile + iYtile * XDC_TILE] = CLUT8[pBuffer[iCnt + iXtile + (jCnt + iYtile) * iWidth]];
// Send framebuffer update pixel data
if (-1 == (send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE, 0)))
{ vCleanSession(XDC_SEND, Errno()); goto _NewSession; }
}
}
#endif
}
}
}
}
vCleanSession("main : Session stop (child stopped)", 0);
goto _NewSession;
}